#include "../RealtimeUI/RealTimeWindow.hpp"
#include "../RealtimeUI/WebsocketClient.hpp"
#include "../include/ProjectClient.hpp"
#include "../RealtimeUI/ServerControl.hpp"
#include <boost/program_options.hpp>
#include <iostream>
#include <thread>
#include <unistd.h>
#include <signal.h>
#include <atomic>
#include <msgpack.hpp>
#include <yaml-cpp/yaml.h>
#include <filesystem>
#include <fmt/format.h>
#include <condition_variable>

namespace po=boost::program_options;

RealTimeWindow* window=nullptr;
ProjectClient* projectClient=nullptr;
ServerControl* serverControl=nullptr;

class CameraItem
{
  public:
    CameraItem(const std::string & Host,const std::string & req,int _index)
      : index(_index)
      , wsClient(Host,req)
    {
    }
    virtual ~CameraItem()
    {
    }
    void GetDecompResult(const char* data,std::size_t size)
    {
      if(window!=nullptr)
      {
        ImageType image;
        image.width=1920;
        image.height=1080;
        image.data_size=1920*1080;
        image.data=(unsigned char*)data;
        window->ShowOriginImage(image,index);
      }
    }
    WebsocketClinet wsClient;
    int index;
};

std::map<std::string,CameraItem*> CameraMap;
WebsocketClinet* handleResultClient;
WebsocketClinet* monitorClient;


bool MainLoopRunFlag=true;

void Handle_SIGINT(int)
{
}

void UnpackMsg(const char* data,std::size_t size,ImageType* result)
{
  msgpack::object_handle result_cameraID;
  msgpack::object_handle result_width;
  msgpack::object_handle result_height;
  msgpack::object_handle result_image;
  std::size_t off=0;
  msgpack::unpack(result_cameraID,data,size,off);
  msgpack::unpack(result_width,data,size,off);
  msgpack::unpack(result_height,data,size,off);
  msgpack::unpack(result_image,data,size,off);
  result->width=result_width->via.i64;
  result->height=result_height->via.i64;
  std::memcpy(result->uuid,result_cameraID->via.str.ptr,result_cameraID->via.str.size);
  result->data_size=result_image->via.bin.size;
  result->data=new unsigned char[result_image->via.bin.size];
  std::memcpy(result->data,result_image->via.bin.ptr,result_image->via.bin.size);
}

void Handle_WSNewData(const char* data,std::size_t size,int index)
{
  ImageType newImage;
  memset(&newImage,0x0,sizeof(newImage));
  UnpackMsg(data,size,&newImage);
  auto ptr=CameraMap.find(newImage.uuid);
  if(ptr!=CameraMap.end() || window!=nullptr)
  {
    ImageType image;
    image.width=1920;
    image.height=1080;
    image.data=(unsigned char*)data;
    image.data_size=size;
    window->ShowImageSingle(image);
    ptr->second->wsClient.WriteText("1",1);
  }
}

std::string ProjectName,TaskName;

void Handle_RecordControl(bool status)
{
  if(!status)
    serverControl->StartRecord(ProjectName,TaskName);
  else
    serverControl->StopRecord();
}

void Handle_WSHandleData(const char* data,std::size_t size)
{
  ImageType newImage;
  memset(&newImage,0x0,sizeof(newImage));
  UnpackMsg(data,size,&newImage);
  ImageType image;
  image.width=1920;
  image.height=1080;
  image.data=(unsigned char*)data;
  image.data_size=size;
  window->ShowImageSingle(image);
  handleResultClient->WriteText("1",1);
}

void Handle_WindowsClose()
{
  MainLoopRunFlag=false;
}

std::mutex mut;

int main(int argc,char** argv)
{
  //锁定，先让UI初始化
  mut.lock();
  std::thread UIThread([&](){
    //启动UI,必须在一个线程中初始化和运行
    auto app=Gtk::Application::create("org.HBTY.RealTimeWindow.test");
    RealTimeWindow rlWin(ProjectName,TaskName);
    window=&rlWin;
    window->funcWindowsCloseCallback=Handle_WindowsClose;
    window->funcRecordCallback=Handle_RecordControl;
    app->signal_activate().connect([&rlWin,&app](){rlWin.test_call(app);});
    mut.unlock();
    app->run();
    window=nullptr;
  });
  mut.lock();

  signal(SIGINT,Handle_SIGINT);

  {
    po::options_description desc("Allowed options");
    desc.add_options()
      ("help","Help message")
      ("project",po::value<std::string>(),"HBTY project name")
      ("task",po::value<std::string>(),"HBTY task name")
    ;
    po::variables_map vm;
    po::store(po::parse_command_line(argc,argv,desc),vm);
    po::notify(vm);
    if(vm.count("help"))
    {
      std::cout<<desc<<std::endl;
      return 0;
    }
    if(vm.count("project") && vm.count("task"))
    {
      ProjectName=vm["project"].as<std::string>();
      TaskName=vm["task"].as<std::string>();
    }
    else
    {
      std::cout<<desc<<std::endl;
      return -1;
    }
  }

  //读取配置文件
  std::string server_URL;
  {
    if(!std::filesystem::exists("RT_conf.yaml"))
      throw std::runtime_error("找不到配置文件 RT_conf.yaml");
    YAML::Node config=YAML::LoadFile("RT_conf.yaml");
    server_URL=config["server_url"].as<std::string>();
    YAML::Node cameraList=config["camera_map"];
    for(int i=0;i<cameraList.size();i++)
    {
      CameraMap.emplace(
        cameraList[i].as<std::string>(),
        new CameraItem(
          server_URL,
          fmt::format("/Camera/Stream?CameraID={}",cameraList[i].as<std::string>()),
          i
        )
      );
    }
  }

  
  ProjectClient project(server_URL);
  projectClient=&project;
  WebsocketClinet HandleResultWS(server_URL,"/Handle/Stream");
  handleResultClient=&HandleResultWS;
  ServerControl sctl(server_URL);
  serverControl=&sctl;


  for(auto & i : CameraMap)
  {
    //i.second->wsClient.WriteText(i.first.c_str(),i.first.length());
    i.second->wsClient.BootReadCallback(std::bind(Handle_WSNewData,std::placeholders::_1,std::placeholders::_2,i.second->index));
  }

  std::string sendData="0";
  handleResultClient->WriteText(sendData.data(),sendData.length());
  handleResultClient->BootReadCallback(Handle_WSHandleData);

  while(sleep(10)==0 && MainLoopRunFlag)
    ;

  //断开连接
  projectClient=nullptr;
  window=nullptr;
  for(auto & i : CameraMap)
    delete i.second;
}
